package com.franklin.ideaplugin.easytesting.controllerclient.client;

import cn.hutool.core.util.ArrayUtil;
import com.franklin.ideaplugin.easytesting.common.entity.MethodInvokeData;
import com.franklin.ideaplugin.easytesting.controllerclient.MethodBuilder;
import com.franklin.ideaplugin.easytesting.controllerclient.ClientMethod;
import com.franklin.ideaplugin.easytesting.controllerclient.beans.MethodData;
import com.franklin.ideaplugin.easytesting.controllerclient.invoke.DefaultControllerMethodInvocationHandler;
import com.franklin.ideaplugin.easytesting.controllerclient.invoke.IControllerMethodInvocationHandler;
import com.franklin.ideaplugin.easytesting.controllerclient.request.IRequestBuilder;
import com.franklin.ideaplugin.easytesting.controllerclient.request.IRequestInvoker;
import lombok.RequiredArgsConstructor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 使用动态代理生成，controller的代理客户端
 *
 * @author Ye Junhui
 * @since 2023/6/28
 */
@RequiredArgsConstructor
public class DefaultControllerClientBuilder implements IControllerClientBuilder {

    private final MethodBuilder methodBuilder;
    private final IRequestBuilder requestBuilder;
    private final IRequestInvoker requestInvoker;

    @Override
    public Object newInstance(Class<?> controllerClazz, Class<?> proxyClazz) {
        List<MethodData> methodDataList = methodBuilder.buildAllMethodData(controllerClazz);

        //映射方法
        Map<Method, MethodData> methodMappings = methodDataList.stream()
                .collect(Collectors.toMap(
                        MethodData::getMethod,
                        Function.identity()
                ));


        IControllerMethodInvocationHandler methodInvocationHandler = new DefaultControllerMethodInvocationHandler(requestBuilder, requestInvoker, methodMappings);

        //生成代理对象
        Object proxyInstance = Proxy.newProxyInstance(proxyClazz.getClassLoader(), new Class<?>[]{proxyClazz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (ArrayUtil.isEmpty(args)){
                    return null;
                }
                MethodInvokeData methodInvokeData = (MethodInvokeData) args[0];
                String targetUrl = (String) args[1];
                Method originMethod = (Method) args[2];
                Object[] originArgs = getOriginArgs(args);
                return methodInvocationHandler.invoke(proxy, method, methodInvokeData, targetUrl, originMethod, originArgs);
            }
        });
        return proxyInstance;
    }

    /**
     * 获取原方法参数
     * @param proxyArgs
     * @return
     */
    private Object[] getOriginArgs(Object[] proxyArgs){
        Object[] originArgs = new Object[proxyArgs.length - 2];
        for (int i = 2; i < proxyArgs.length; i++) {
            originArgs[i - 2] = proxyArgs[i];
        }
        return originArgs;
    }
}
